#include "libbackuper.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ftw.h>
#include <time.h>
#include <pthread.h>
#include <math.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int dim = 100;
int current = 0;
int length;

char **sources;
char **destinations;
char *source;
char *destination;
int *mutex;

void allocateArray(void);
void setSource(char*);
void setDestination(char*);
void allocateMutex(void);
void copyFiles(void);
void copy(char*, char*);
void* copyFile(void*);
char *getServerRoot(void);
char *getTimeFolder(void);
char *getFull(int);
char *getPureFile(const char*);
int fileExists(char*);
int addFile(const char*, const struct stat*, int);
int addFilesToQueue();

void error(const char *message, ...) {
	va_list lista;
	
	char c_baf, *s_baf;
	int i_baf;
	double d_baf;
	fprintf(stderr, "ERROR: ");
	va_start(lista, message);
	
	while( *message != '\0' ) {
		if( *message != '%' ) {
			fprintf(stderr, "%c", *message++);
			continue;
		}
		switch( *(message+1) ) {
			case 'f':
				d_baf = va_arg(lista, double);
				fprintf(stderr, "%g", d_baf);
				break;
			case 'i':
			case 'd':
				i_baf = va_arg(lista, int);
				fprintf(stderr, "%d", i_baf);
				break;
			case 'c':
				c_baf = (char)va_arg(lista, int);
				fprintf(stderr, "%c", c_baf);
				break;
			case 's':
				s_baf = va_arg(lista, char*);
				fprintf(stderr, "%s", s_baf);
				break;
			case '%':
				fprintf(stderr, "%%");
				break;
		}
		message += 2;
	}
	
	va_end(lista);
	perror("\nStatus");
	exit(EXIT_FAILURE);
}

// Initialization of global variables
char* getServerRoot() {
	char *base = getenv("HOME");
	char *server = (char*)malloc((12+strlen(base))*sizeof(char));
	if( server == NULL ) {
		error("Cannot allocate memory for server root");
	}
	sprintf(server, "%s/public_html", base);
	return server;
}
char* getFull(int old) {
	char *month = (char*)malloc(2*sizeof(char));
	if( month == NULL ) {
		error("Cannot allocate memory for month");
	}
	if( old < 10 ) {
		sprintf(month, "0%d", old);
	}
	else {
		sprintf(month, "%d", old);
	}
	return month;
}
char* getTimeFolder() {
	time_t base;
	time(&base);
	struct tm *currentTime = localtime(&base);
	char *strtime = (char*)malloc(16*sizeof(char));
	if( strtime == NULL ) {
		error("Cannot allocate memory for time string");
	}
	int year = currentTime->tm_year+1900;
	char* month = getFull(currentTime->tm_mon+1);
	char* day = getFull(currentTime->tm_mday);
	char* hour = getFull(currentTime->tm_hour);
	char* minute = getFull(currentTime->tm_min);
	sprintf(strtime, "%d_%s_%s-%s:%s", year, month, day, hour, minute);
	return strtime;
}
int fileExists(char *path) {
	struct stat bafer;
	return !stat(path, &bafer);
}
void allocateFilesArray() {
	sources = (char**)malloc(dim*sizeof(char*));
	if( sources == NULL ) {
		error("Cannot allocate memory for sources queue");
	}
	destinations = (char**)malloc(dim*sizeof(char*));
	if( destinations == NULL ) {
		error("Cannot allocate memory for destinations queue");
	}
}
void setSource(char *project) {
	char *server = getServerRoot();
	length = strlen(project)+strlen(server)+1;
	source = (char*)malloc(length*sizeof(char));
	if( source == NULL ) {
		error("Cannot allocate memory for source folder path");
	}
	sprintf(source, "%s/%s", server, project);
	if( !fileExists(source) ) {
		error("Source directory doesn't exist");
	}
}
void setDestination(char *project) {
	char *server = getServerRoot();
	char *timeFolder = getTimeFolder();
	int stringLength = strlen(server) + strlen(timeFolder) + strlen(project) + 10;
	destination = (char*)malloc(stringLength*sizeof(char));
	sprintf(destination, "%s/.backup/%s/%s", server, project, timeFolder);
}

// Find files to copy
char *getPureFile(const char *path) {
	return strdup(path+length+1);
}
void addToDestination(const char *path) {
	char *bafer = (char*)calloc(strlen(destination)+strlen(path)+16, sizeof(char));
	if( bafer == NULL ) {
		error("Cannot allocate memory for destination bafer string");
	}
	sprintf(bafer, "\"%s/%s\"", destination, path);
	destinations[current-1] = bafer;
}
char* escapeFilename(const char *path) {
	char *bafer = (char*)calloc(strlen(path)+2, sizeof(char));
	if( bafer == NULL ) {
		error("Cannot allocate memory for escaped filename");
	}
	sprintf(bafer, "\"%s\"", path);
	return bafer;
}
int addFile(const char *fpath, const struct stat *sb, int typeflag) {
	if( S_ISREG(sb->st_mode) ) {
		sources[current++] = escapeFilename(fpath);
		if( current >= dim ) {
			dim += 100;
			sources = (char**)realloc(sources, dim*sizeof(char*));
			if( sources == NULL ) {
				error("Cannot reallocate memory for sources queue");
			}
			destinations = (char**)realloc(destinations, dim*sizeof(char*));
			if( destinations == NULL ) {
				error("Cannot reallocate memory for destinations queue");
			}
		}
		addToDestination(getPureFile(fpath));
	}
	return 0;
}
int addFilesToQueue() {
	return ftw(source, addFile, 10);
}

char *getCommand(int index) {
	char *bafer = (char*)calloc(4096, sizeof(char));
	if( bafer == NULL ) {
		error("Cannot allocate memory for command");
	}
	sprintf(bafer, "bash -c cp %s %s", sources[index], destinations[index]);
	return bafer;
}
// Copy files with different threads
void* copyFile(void *arg) {
	int i = *(int*)arg;
	if( i > current ) {
		return (void*)0;
	}
//	system(getCommand(i));
	printf("%s\n", getCommand(i));
	return (void*)0;
}
void copyFiles() {
	printf("Started copying %d files\n", current);
	pthread_t *threads = (pthread_t*)malloc(current*sizeof(pthread_t));
	if( threads == NULL ) {
		error("Cannot allocate memory for threads");
	}
	int i, *arg;
	int err;
	for(i = 0; i < current; i++) {
		arg = (int*)malloc(sizeof(int));
		if( arg == NULL ) {
			error("Cannot allocate memory for allocate");
		}
		*arg = i;
		err = pthread_create(&threads[i], NULL, copyFile, arg);
		if( err != 0 ) {
			error("Thread error: %s", strerror(err));
		}
		free(arg);
	}
	for(i = 0; i < current; i++) {
		err = pthread_join(threads[i], NULL);
		if( err != 0 ) {
			error("Thread error: %s", strerror(err));
		}
	}
	printf("Finished copying files\n");
}

// Main function that is initialize variables and find and copies all files
void run(int argc, char **argv) {
/*	if( argc != 2 ) {
		error("Missing argument");
	}
*/	
	allocateFilesArray();
	setSource("Travelisima");
	setDestination("Travelisima");
	addFilesToQueue();
	copyFiles();
}
